#include <gtk/gtk.h>

#include <gdk/gdkkeysyms.h>

#include "qaholom.h"
#include "qahcontrols.h"
#include "qahcontainer.h"
#include "qahsighandlers.h"
#include "drawingtool.h"

#include "zoota-common.h"

//////////////////////////////////////////////////////////////
// Global Variable Definition

TRADINGDATA  twStockData;
vector<D5AVGPRICE> tw5DaysAveragePrice;
vector<D5AVGPRICE> tw21DaysAveragePrice;

//////////////////////////////////////////////////////////////
// Member Functions of ZoTeklineChart

gboolean ZoTeklineChart::onButtonPressed(GdkEventButton *event) 
{
   GtkAllocation alloc = egoWidget->allocation;
   
   alloc.x = 0;
   alloc.y = 0;
      
   // invalidates the whole window
   gdk_window_invalidate_rect(egoWidget->window, &alloc, FALSE);
   
   GtkAllocation alloc1 = egoWidget->allocation;
   
   alloc1.x = 0;
   alloc1.y = 0;
   
   gdk_window_invalidate_rect(egoWidget->window, &alloc1, FALSE);

   switch (event->button)
   {
      case 1: start = min(start + 1, (int) twStockData.size() - 1); break;
      case 2: start = 0; break;
      case 3: start = max(start - 1, 0); break;
   }
   
   return TRUE;
}

gboolean ZoTeklineChart::onExpose(GdkEventExpose *event)
{
   static cairo_t *cr = NULL;
   static int count = 0;
   cairo_matrix_t  matrix;
   DrawingTool dtool;

   double width  = egoWidget->allocation.width;
   double height = egoWidget->allocation.height;
   
   cr = gdk_cairo_create (egoWidget->window); 
   //dtool.create(egoWidget);
   
   // draw background ...
   cairo_set_source_rgba(cr, 0,0,0,1);
   cairo_rectangle(cr, 0, 0, width, height);
   cairo_fill(cr);
   
   
   cairo_set_line_width (cr, 3);
   cairo_set_source_rgba (cr, 1,0.7,0.2,0.5);
   cairo_new_path (cr);

   // >>> set coordinate mapping 
   cairo_translate(cr, 0, height); // move the orign point to (0, height)
   cairo_get_matrix(cr, &matrix);

   matrix.yy = -1.0;  // change the y axis direction
   cairo_set_matrix(cr, &matrix);


   double maxVolume = 0;
   double minVolume = 999999.99;
      
   for (int i = 0 ; i < min(120, (int) twStockData.size() - start); i++) 
   {
      maxVolume = max(twStockData[i + start].txVolume, maxVolume);
      minVolume = min(twStockData[i + start].txVolume, minVolume);   
   }   

   DTRECT rect(5, 5, height - 10, width - 10);
   double ratio = ((double) height) / (maxVolume - minVolume);
   double day = 2; height -= 20;
   
   for (int i = 0 ; i < min(48, (int) twStockData.size() - start - i); i++) 
   {
      TRADINGDAY& today = twStockData[i + start];
      
      if (i == 0)
         cairo_move_to (cr, day + 2, (today.txVolume - minVolume) * ratio + rect.Y);
      else
         cairo_line_to (cr, day += 18,  (today.txVolume - minVolume) * ratio + rect.Y);
         
      //dtool.setSourceRGBA(RGBA_GREEN);
      //dtool.moveTo(ptYesterday.X, (yesterdayP5.averagePrice - minPrice) * ratio);
      //dtool.lineTo(ptToday.X,     (todayP5.averagePrice - minPrice) * ratio);
      //dtool.stroke();
         
   }   

   return FALSE;
}

//////////////////////////////////////////////////////////////
// Member Functions of ZoMarketChart

gboolean ZoMarketChart::onButtonPressed(GdkEventButton *event) 
{
   GtkAllocation alloc = egoWidget->allocation;
   
   alloc.x = 0;
   alloc.y = 0;
      
   // invalidates the whole window
   gdk_window_invalidate_rect(egoWidget->window, &alloc, FALSE);
   
   GtkAllocation alloc1 = egoWidget->allocation;
   
   alloc1.x = 0;
   alloc1.y = 0;
   
   gdk_window_invalidate_rect(egoWidget->window, &alloc1, FALSE);

   switch (event->button)
   {
      case 1: start = min(start + 1, (int) twStockData.size() - barCount); break;
      case 2: start = 0; break;
      case 3: start = max(start - 1, 0); break;
   }
   
   
   return TRUE;
}

gboolean ZoMarketChart::onMouseMoved(GdkEventButton *event)
{
   mpos.X = event->x;
   mpos.Y = event->y;
   
   GtkAllocation alloc = egoWidget->allocation;
   
   alloc.x = alloc.y = 0;
   // invalidates the whole window
   gdk_window_invalidate_rect(egoWidget->window, &alloc, FALSE);

   return TRUE;
}


gboolean ZoMarketChart::onKeyPressed(GdkEventKey  *event)                                  
{
   GtkAllocation alloc = egoWidget->allocation;
   
   alloc.x = 0;
   alloc.y = 0;
   
   // invalidates the whole window
   gdk_window_invalidate_rect(egoWidget->window, &alloc, FALSE);
   
   // invalidates the volume window
   GtkAllocation alloc1 = egoWidget->allocation;
   
   alloc1.x = 0;
   alloc1.y = 0;
   
   gdk_window_invalidate_rect(egoWidget->window, &alloc1, FALSE);


   switch (event->keyval) 
   {   
      case GDK_Q:
      case GDK_q:
         gtk_main_quit();
         break;
      case GDK_Right:
         start = min(start + 1, (int) twStockData.size() - barCount);
         break;
      case GDK_Left:
         start = max(start - 1, 0);
         break;
      default:
         break;
   }
   
   return TRUE;
}                                 

gboolean test_expose(GtkWidget* widget, GdkEventExpose *event)
{
   DrawingTool  display;
   cairo_surface_t *surface;
   int png_width, png_height;
   
   surface = cairo_image_surface_create_from_png("good.png");   

   png_height = cairo_image_surface_get_height(surface);
   png_width  = cairo_image_surface_get_width(surface);
   
   display.create(widget);
   display.setAttribute(DTST_LINE_WIDTH, 5);

   //display.setSourceRGBA(RGBA_RED);
   display.setSource(surface, 100,100);
   display.paint();

   cairo_surface_destroy(surface);
   
//   display.drawLine(  0,   0, 50, 50);   
//   display.setAttribute(DTST_COORDINATION_SYSTEM, CHART_COORDINATION);
   
   return FALSE;  
}

vector<double> calculateLeftScale(double maxPrice, double minPrice)
{
   // >>> computing the left meter
   vector<double> numbers, perfect_numbers;
   char buf[20];
   bool clear_digit = false;
   
   sprintf(buf, "%.2f", maxPrice);
   
   for (int i = 0; i < strlen(buf); i++)
   {
      if (clear_digit)
         buf[i] = (buf[i] == '.') ? '.' : '0';
      else 
      {   
         if (buf[i] != '0' || buf[i] != '.' || buf[i] != '-')
         {
            buf[i] = '1';
            clear_digit = true;
            continue;
         }
      }
   }
   
   double ceiling, distance, floor;
   int devided = 0;
   bool perfect = false;
   double old_distance;
   
   old_distance = distance = atof(buf);
   // clip section by unit number
   
   while (numbers.size() <= 6) 
   {
      ceiling  = distance * (((int) (maxPrice / distance))  + 1);
      floor    = distance * ((int) (minPrice / distance));
   
      double f = ceiling;
      
      perfect_numbers = numbers;
      numbers.clear();

      while (f >= floor) 
      {
         numbers.push_back(f);
         f -= distance;
      }
      
      numbers.push_back(f); // the floor

      switch (devided) 
      {
         case 0: distance = old_distance / 2;  break;
         case 1: distance = old_distance / 4;  break;
         case 2: distance = old_distance / 8;  break;
         case 3: distance = old_distance / 10; break;
         case 4:
            old_distance /= 10;
            distance = old_distance / 2;
            devided  = 1;
         break;
      }
      
      devided++;
   }   
         
   return perfect_numbers;
}
gboolean ZoMarketChart::onExpose(GdkEventExpose *event)
{
   //return test_expose( egoWidget, event);
   
   bool mouseClicked = true;
   static int count = 0;

   double width  = egoWidget->allocation.width;
   double height = egoWidget->allocation.height;
   
   DrawingTool dtool;
   cairo_matrix_t   matrix;
   
   cairo_surface_t *surface, *surface1;

   // >>> create surface buffer
   surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, (int) width, (int) height);   

   dtool.create(surface);
   dtool.setAttribute(DTST_LINE_WIDTH, 3);

   // drawing background   
   dtool.setSourceRGBA(0.05, 0.05, 0.05, 1);
   dtool.rectangle(0, 0, width, height);
   dtool.fill();

   //.:: Compute Max & Min value ::. 
   
   maxPrice = 0;
   minPrice = 999999.99;
      
   for (int i = 0 ; i < min(60, (int) twStockData.size() - start); i++) 
   {
      maxPrice = max(twStockData[i + start].txHigh, maxPrice);
      minPrice = min(twStockData[i + start].txLow, minPrice);
      minPrice = min(tw5DaysAveragePrice[i + start].averagePrice, minPrice);   
      minPrice = min(tw21DaysAveragePrice[i + start].averagePrice, minPrice);   
   }   

   // .:: Drawing Technical Line ::.

   DTRECT chartRect(0, 0, width, height);
   double spacing = 20, ratio;
   double dayWidth = 10;
   double posX, posY;

   // >>> calculate drawing area & pixel / value scaling      
   chartRect.inflate(-60, -20, -20, -20);
   ratio = chartRect.height / (maxPrice - minPrice);
 
   // >>> drawing the chart outline
   dtool.setSourceRGBA(1, 0.2, 0.2, 0.6);
   dtool.rectangle(chartRect.X - 3, chartRect.Y - 3, chartRect.width + 6, chartRect.height + 6);
   dtool.stroke();

   // >>> set coordinate mapping 
   dtool.setAttribute(DTST_COORDINATION_SYSTEM, CHART_COORDINATION);

   // >>> drawing technical line
   DTPOINT center(width * 0.5, height * 0.5);
   DTRECT  bar(0,0,0,0);
   int current = 0;
   int day_slice_size = 18, bar_count = 0, bar_width = 10;
   
   bar_count = (int) (chartRect.width / day_slice_size);
   
   if (barCount == 0)
      barCount = bar_count;

   // magic number 48 : means the total days show on the screen
   
   for (int i = 0; i < min(bar_count, (int) twStockData.size() - start); i++) 
   {
      TRADINGDAY& today       = twStockData[i + start];
      TRADINGDAY& yesterday   = twStockData[((i + start) > 0) ? i + start - 1 : 0];
      
      // 2 is the distance from the left border.
      bar.X      = chartRect.X + (i * day_slice_size) + 2; 
      bar.Y      = (today.txOpen - minPrice) * ratio + chartRect.Y;
      bar.width  = bar_width;
      bar.height = (today.txClose - today.txOpen) * ratio;
   
      if (today.txClose > yesterday.txClose)
         dtool.setSourceRGBA(RGBA_RED);
      else
         dtool.setSourceRGBA(RGBA_WHITE);   

      dtool.rectangle(bar.X, bar.Y, bar.width, bar.height);
      dtool.fill();

      // =========================================================
      // >>> drawing upper shadow line and lower shadow line
      DTPOINT ptTop, ptBottom;

      ptTop.X = bar.X + bar.width / 2;
      ptTop.Y = bar.Y + ((bar.height > 0) ? bar.height : 0);
   
      dtool.moveTo(ptTop.X, ptTop.Y);

      ptTop.Y = (today.txHigh - minPrice) * ratio + chartRect.Y;
      
      dtool.lineTo(ptTop.X, ptTop.Y);
      dtool.stroke();

      ptBottom.X = bar.X + bar.width / 2;
      ptBottom.Y = bar.Y + ((bar.height < 0) ? bar.height : 0);
   
      dtool.moveTo(ptBottom.X, ptBottom.Y);

      ptBottom.Y = (today.txLow - minPrice) * ratio;
   
      dtool.lineTo(ptBottom.X, ptBottom.Y + chartRect.Y);
      dtool.stroke();
   
      // =========================================================
      // >>> drawing average price line
      D5AVGPRICE& todayP5     = tw5DaysAveragePrice[i + start];
      D5AVGPRICE& yesterdayP5 = tw5DaysAveragePrice[((i + start) > 0) ? i + start - 1 : 0];

      D5AVGPRICE& todayP21     = tw21DaysAveragePrice[i + start];
      D5AVGPRICE& yesterdayP21 = tw21DaysAveragePrice[((i + start) > 0) ? i + start - 1 : 0];


      DTPOINT ptToday, ptTodayP21;
      DTPOINT ptYesterday, ptYesterdayP21;
      
      ptYesterday.X = (chartRect.X + ((i - 1) * 18) + 2) + bar.width / 2;
      ptYesterday.Y = (yesterdayP5.averagePrice - minPrice) * ratio + chartRect.Y;
      
      ptToday.X = ptTop.X;
      ptToday.Y = (todayP5.averagePrice - minPrice) * ratio + chartRect.Y;
      
      ptTodayP21.X     = ptToday.X;
      ptYesterdayP21.X = ptYesterday.X;
      
      ptYesterdayP21.Y = (yesterdayP21.averagePrice - minPrice) * ratio + chartRect.Y;
      ptTodayP21.Y     = (todayP21.averagePrice - minPrice) * ratio + chartRect.Y;
      
      // we don't want to draw the first day to its yesterday ...
      if (i != 0) 
      {
        
         // 5-DAT AVERAGE LINE
         dtool.setSourceRGBA(RGBA_GREEN);
         dtool.moveTo(ptYesterday.X, ptYesterday.Y);
         dtool.lineTo(ptToday.X, ptToday.Y);
         dtool.stroke();
         // 21-DAY AVERAGE LINE
         dtool.setSourceRGBA(0.8, 0.8, 0.2, 1);
         dtool.moveTo(ptYesterdayP21.X, ptYesterdayP21.Y);
         dtool.lineTo(ptTodayP21.X, ptTodayP21.Y);
         dtool.stroke();
      }

      // =========================================================
      // >>> Determine the mouse x-position.
      double dist = bar.width / 2 + 18 / 2;
      
      if (mpos.X < ptTop.X + dist && mpos.X > ptTop.X - dist) 
      {
         mpos.X = ptTop.X;
         current = i + start;
      }
   } // end of FOR-BLOCK

   // .:: TRY LAYER CONTROL ::.
   
   surface1 = dtool.getTarget();
   
   DrawingTool  display;
   
   display.create(egoWidget);
   display.setSource(surface1);
   display.paint();
   
   // >>> drawing simple trading information at the left-top corner
   display.setSourceRGBA(0.8, 0.8, 0.3, 1);
   
   cairo_text_extents_t extents;
   TRADINGDAY& selected = twStockData[current];

   char utf8[128];
   double x,y;

   display.setAttribute(DTST_COORDINATION_SYSTEM, CHART_COORDINATION);
   display.setSourceRGBA(0, 1, 0.5, 0.7);
   
   // >>> setting fonts   
   display.selectFont("Tohoma", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
   display.setFontSize(13);
   display.moveTo(10, height - 13);
   sprintf(utf8, "max: %.2f, min: %.2f", maxPrice, minPrice);

   cairo_get_font_matrix(display.cairoObj, &matrix);
   matrix.yy *= -1;
   cairo_set_font_matrix(display.cairoObj, &matrix);

   cairo_show_text(display.cairoObj, utf8);
   display.setFontSize(12.0);

   //cairo_show_text(display.cairoObj, utf8);
   display.setAttribute(DTST_LINE_WIDTH, 3);

   vector<double> ruler_numbers;
   
   ruler_numbers = calculateLeftScale(maxPrice, minPrice);

   cairo_get_font_matrix(display.cairoObj, &matrix);
   matrix.yy *= -1;
   cairo_set_font_matrix(display.cairoObj, &matrix);
            
   
   for (int i = 0; i < ruler_numbers.size(); i++)
   {
      char buf[20];
      DTPOINT pt;
      
      pt.X = chartRect.X - 35;
      pt.Y = chartRect.Y + (ruler_numbers[i] - minPrice) * ratio;

      sprintf(buf, "%.1f", ruler_numbers[i]);

      display.moveTo(pt.X, pt.Y);
      //display.showText(buf);
      cairo_show_text(display.cairoObj, buf);
   }  

   //g_print("ruler number size = [%d]\n", ruler_numbers.size());
   //display.fill();
   
   
   cairo_surface_destroy(surface);
   return FALSE;
}

